<template>
  <div class="article-content-block w-1200">
    <div class="article-info-block" v-if="articleDetail.html">
      <div class="article-content">
        <div class="article-suspended-panel">
          <div
            :style="{ backgroundImage: `url(${articleDetail.avatar})` }"
            class="modal focus"
            :class="{ 'no-bg': !articleDetail.avatar, hidden: !focusBtnVisible }"
            @click="updateUserFocus(!isFocususer)"
          >
            <span class="iconfont ri-user-3-fill" v-if="!articleDetail.avatar"></span>
            <i class="focus-text">{{ isFocususer ? '已关注' : '关注' }}</i>
          </div>
          <div
            class="modal"
            :class="{ liker: articleDetail.isLiker }"
            @click="isLikeArticle(articleDetail)"
          >
            <span class="iconfont ri-thumb-up-fill"></span>
            <em v-if="articleDetail.likeCounts">{{
              articleDetail.likeCounts
            }}</em>
          </div>
          <div class="modal">
            <a href="#article-comments">
              <span class="iconfont ri-message-3-fill"></span>
              <em v-if="articleDetail.commentCounts">{{
                articleDetail.commentCounts
              }}</em>
            </a>
          </div>
          <div
            class="modal"
            :class="{ collecter: articleDetail.isFlower }"
            @click="isCollectArticle(articleDetail)"
          >
            <span class="iconfont ri-star-fill"></span>
          </div>
        </div>
        <div class="title">{{ articleDetail.articleTitle }}</div>
        <div class="user-model">
          <HeadAvatar
            :width="36"
            :height="36"
            :to="`/user/${articleDetail.userId}`"
            :img="articleDetail.avatar"
          />
          <div class="info">
            <p class="nickname">
              <nuxt-link
                target="_blank"
                :to="`/user/${articleDetail.userId}`"
                >{{ articleDetail.nickname }}</nuxt-link
              >
              <Level :level="calcLevelFromExp(articleDetail.userExp)" />
            </p>
            <p class="time">
              {{
                articleDetail.createTime
                  .replace('-', '年')
                  .replace('-', '月')
                  .replace(' ', '日 ')
              }}
              <span>· 阅读 {{ articleDetail.viewCounts | splitNumber }}</span>
              <a-tag
                style="margin-left: 12px;"
                color="red"
                v-if="articleDetail.status !== '2'">{{ articleDetail.status === '3' ? '审核不通过' : '待审核' }}</a-tag>
            </p>
          </div>
        </div>
        <div
          class="markdown-article-show-content v-note-wrapper markdown-body">
          <div class="v-note-show">
            <div class="v-show-content" v-html="articleDetail.html" v-copy></div>
          </div>
        </div>
        <div class="article-tag">
          <span
            v-for="(item, index) in articleDetail.tag?.split(';')"
            :key="index"
            >{{ tagMap[item] || item }}</span
          >
        </div>
        <div class="reprint-statement" v-if="articleDetail.linkUrl">
          <p class="title">转载声明</p>
          <div class="label-item">
            <div class="label">原作者：</div>
            <div class="value"><p>匿名</p></div>
          </div>
          <div class="label-item">
            <div class="label">转载自：</div>
            <div class="value">
              <a :href="articleDetail.linkUrl" target="_blank">《{{ articleDetail.articleTitle }}》</a>
              <em>，如有侵权，请联系本站删除。</em>
            </div>
          </div>
        </div>
      </div>
      <ArticleComment :article-detail="articleDetail" />
      <ArticleRecommend :tags="articleDetail.tag" :articleId="articleDetail.id" />
    </div>
    <div
      v-if="articleDetail.html"
      style="margin-left: 12px; width: 300px; flex-shrink: 0;">
      <div class="author-info-block">
        <div class="author-info">
          <HeadAvatar
            :width="52"
            :height="52"
            :to="`/user/${articleDetail.userId}`"
            :img="articleDetail.avatar" />
          <div class="nickname">
            <p>{{ articleDetail.nickname }}</p>
            <span>{{ articleDetail.userDescription || '作者什么都没有留下！' }}</span>
          </div>
        </div>
        <div class="auth-charts linear-border-top">
          <div class="user-statistic">
            <div class="user-statistic-item">
              <span>{{ statistic.articleTotal | splitNumber }}</span>
              <p>文章</p>
            </div>
            <div class="user-statistic-item">
              <span>{{ statistic.articleViewCountTotal | splitNumber }}</span>
              <p>阅读</p>
            </div>
            <div class="user-statistic-item">
              <span>{{ statistic.userGetAgreeTotal | splitNumber }}</span>
              <p>点赞</p>
            </div>
          </div>
          <div
            v-if="userInfo.id !== articleDetail.userId"
            class="focus-user">
            <a-button
              v-if="!isFocususer"
              type="primary"
              icon="user-add"
              @click="updateUserFocus(true)"
              >关注</a-button
            >
            <a-button
              v-else
              type="primary"
              icon="check"
              @click="updateUserFocus(false)"
              >已关注</a-button
            >
            <a-button
              type="dashed"
              icon="mail"
              @click="handleImPerson(articleDetail.userId)"
              >私信</a-button
            >
          </div>
        </div>
      </div>
      <div class="sticky-modal">
        <div class="tocs-info-block" v-show="tocs.length">
          <div class="title"><p class="linear-border-bottom">目录</p></div>
          <ul class="toc-list gloab-scroll-bar">
            <li
              v-for="(item, index) in tocs"
              :key="index"
              class="toc-list-item"
              :class="{ active: item.active, h2: item.depth === 1, h3: item.depth > 1 }"
              :data-id="item.href"
            >
              <span
                @click.stop="scrollToPosition(item)"
                :title="item.name"
              >{{ item.name }}</span>
            </li>
          </ul>
        </div>
        <AuthorRecommend :userId="articleDetail.userId" :articleId="articleDetail.id" />
      </div>
    </div>
    <a-empty v-if="!articleDetail.html" description="This page could not be found" />
  </div>
</template>

<script>
import { mapState } from 'vuex'
import { tagMap } from '~/config/optionMap'
import ArticleComment from './components/ArticleComments.vue'
import ArticleRecommend from './components/ArticleRecommend.vue'
import AuthorRecommend from './components/AuthorRecommend'
import { validateUniqId, calcLevelFromExp, getMarkdownTos, removeScriptAndLink, getElementDistanceFromTop, debounce } from '~/util'
export default {
  name: 'PostIndex',
  components: { ArticleComment, ArticleRecommend, AuthorRecommend },
  layout: 'BaseLayout',
  validate ({ params }) {
    return validateUniqId(params.id)
  },
  async asyncData ({ $axios, params }) {
    const { data: articleDetail } = await $axios.get(`/gimishare/api/v1/articles/articleinfo?id=${params.id}`)
    if (!articleDetail) return
    const focusId = articleDetail.userId
    const { data: isFocususer } = await $axios.get(`/gimishare/api/v1/users/isfocususer?focusId=${focusId}`)
    return {
      articleDetail,
      isFocususer
    }
  },

  head () {
    return {
      title: this.articleDetail.articleTitle,
      meta: [
        { charset: 'utf-8' },
        {
          hid: 'description',
          name: 'description',
          content: this.articleDetail.articleTitle
        },
        {
          hid: 'keywords',
          name: 'keywords',
          content: this.articleDetail.tag?.split(';')
            .map((t) => tagMap[t] || t).join(',')
        }
      ],
      link: [
        {
          rel: 'stylesheet',
          href: '/md/markdown/' + this.articleDetail.theme + '.min.css'
        },
        {
          rel: 'stylesheet',
          href: '/md/highlightjs/styles/' + this.articleDetail.codeStyle + '.min.css'
        },
        {
          rel: 'stylesheet',
          href: '/md/katex/katex.min.css'
        }
      ]
    }
  },

  data () {
    return {
      tagMap,
      articleDetail: {},
      isFocususer: false,
      statistic: {},
      tocs: [],
      scrollTimer: null,
      visibleElementId: [],
      clickState: false,
      focusBtnVisible: false,
      calcLevelFromExp
    }
  },

  created () {
    if (this.userInfo) {
      this.generateHistory()
    }
    this.userRadarStatistic()
  },

  mounted () {
    this.$nextTick(() => {
      document.body.oncopy = (event) => {
        event.preventDefault()
        let copyFont = window.getSelection(0).toString()
        if (copyFont.length > 10) {
          copyFont = `${copyFont}\n\n作者：${this.articleDetail.nickname}\n链接：${window.location.href}\n来源：GimiShare`
        }
        return event.clipboardData
          ? event.clipboardData.setData('text', copyFont)
          : window.clipboardData.setData('text', copyFont)
      }
      this.tocs = getMarkdownTos(document.querySelector('.markdown-article-show-content'))
      this.bindIntersectionObserver()
      const markdownBody = document.querySelector('.markdown-body')
      if (markdownBody) {
        markdownBody.onclick = (e) => {
          if (e.target.nodeName === 'IMG') {
            const src = e.target.getAttribute('src')
            this.handlePreview({ url: src })
          }
        }
      }
    })
    this.bindFocusIntersectObserver()
  },

  methods: {
    async userRadarStatistic () {
      try {
        const { data } = await this.$axios.post(
          `/gimishare/api/v1/statistics/actioncounts?userId=${this.articleDetail.userId}`
        )
        this.statistic = data.reduce((prev, cur) => {
          prev[cur.key] = cur.value
          return prev
        }, {})
      } catch (error) {
        console.log(error)
      }
    },

    /**
     * article liker
     * @param { Object } articleItem
     */
    async isLikeArticle (articleItem) {
      if (!this.userInfo) {
        return this.$store.commit('UPDATE_LOGIN_VISIBLE', true)
      }
      try {
        const API = articleItem.isLiker
          ? '/users/unagreearticle'
          : '/users/agreearticle'
        await this.$axios.get(
          `/gimishare/api/v1${API}?articleId=${articleItem.id}&uid=${articleItem.userId}`
        )
        if (articleItem.isLiker) {
          articleItem.likeCounts -= 1
        } else {
          articleItem.likeCounts += 1
        }
        articleItem.isLiker = !articleItem.isLiker
      } catch (error) {
        console.log(error)
      }
    },

    /**
     * article collect
     * @param { Object } articleItem
     */
    async isCollectArticle (articleItem) {
      if (!this.userInfo) {
        return this.$store.commit('UPDATE_LOGIN_VISIBLE', true)
      }
      try {
        const API = articleItem.isFlower
          ? '/users/uncollectarticle'
          : '/users/collectarticle'
        await this.$axios.get(
          `/gimishare/api/v1${API}?articleId=${articleItem.id}&uid=${articleItem.userId}`
        )
        articleItem.isFlower = !articleItem.isFlower
      } catch (error) {
        console.log(error)
      }
    },

    /**
     * user focus user
     * @param { Boolean } state true - focus  false - unfocus
     */
    async updateUserFocus (state) {
      if (!this.userInfo) {
        return this.$store.commit('UPDATE_LOGIN_VISIBLE', true)
      }
      try {
        const API = state ? '/users/focususer' : '/users/unfocususer'
        const focusId = this.articleDetail.userId
        await this.$axios.get(`/gimishare/api/v1${API}?focusId=${focusId}`)
        this.isFocususer = !this.isFocususer
      } catch (error) {
        console.log(error)
      }
    },

    handleImPerson (userId) {
      if (!this.userInfo) {
        return this.$store.commit('UPDATE_LOGIN_VISIBLE', true)
      }
      this.$router.push({
        path: '/notification/im',
        query: {
          participantId: userId
        }
      })
    },

    generateHistory () {
      this.$axios.post('/gimishare/api/v1/history/generate', {
        uid: this.articleDetail.userId,
        articleId: this.articleDetail.id
      })
    },

    imagePreview (e) {
      const img = e.getAttribute('src')
      this.$store.commit('UPDATE_PREVIEW_IMGSRC', img)
    },

    scrollToPosition (navigatorItem) {
      this.clickState = true
      clearTimeout(this.scrollTimer)
      for (let i = 0; i < this.tocs.length; i++) {
        this.$set(this.tocs[i], 'active', navigatorItem.href === this.tocs[i].href)
      }
      const ele = document.querySelector(`.markdown-article-show-content #${navigatorItem.href}`)
      const scrollTop = document.documentElement.scrollTop
      const offsetTop = getElementDistanceFromTop(ele)
      const maxScrollTop = document.querySelector('#__layout').clientHeight - document.documentElement.clientHeight
      const scrollPostion = Math.min(offsetTop - 80, maxScrollTop)
      this.scrollDocument(scrollPostion, scrollPostion < scrollTop ? 'up' : 'down')
    },

    /**
     * 滚动到指定位置
     * @param { position } string
     * @param { direction } string
     */
    scrollDocument (position, direction) {
      const scrollTop = document.documentElement.scrollTop
      if (direction === 'up') {
        const nextScrollTop = Math.max(position, scrollTop - (scrollTop - position) * 0.18)
        document.documentElement.scrollTop = nextScrollTop
        if (nextScrollTop > position) {
          this.scrollTimer = setTimeout(() => {
            this.scrollDocument(position, direction)
          }, 16)
        } else {
          this.clickState = false
        }
      } else {
        const nextScrollTop = Math.min(position, Math.ceil(scrollTop + (position - scrollTop) * 0.18))
        document.documentElement.scrollTop = nextScrollTop
        if (nextScrollTop < position) {
          this.scrollTimer = setTimeout(() => {
            this.scrollDocument(position, direction)
          }, 16)
        } else {
          this.clickState = false
        }
      }
    },

    bindIntersectionObserver () {
      const c = debounce(this.setTocsCurrentActive, 10)
      const observer = new IntersectionObserver((entries, observer) => {
        entries.forEach(entry => {
          if (entry.intersectionRatio === 1) {
            this.visibleElementId[entry.boundingClientRect.top < 200 ? 'unshift' : 'push'](entry.target.id)
          } else {
            const idx = this.visibleElementId.findIndex(id => id === entry.target.id)
            if (idx > -1) {
              this.visibleElementId.splice(idx, 1)
            }
          }
          if (!this.clickState) {
            c(this.visibleElementId)
          }
        })
      }, { threshold: 1, rootMargin: '-80px 0px 0px 0px' })
      for (let i = 0; i < this.tocs.length; i++) {
        observer.observe(document.querySelector(`.markdown-article-show-content #${this.tocs[i].href}`))
      }
    },

    setTocsCurrentActive (ids) {
      if (!ids?.length) return
      const id = ids[0]
      for (let i = 0; i < this.tocs.length; i++) {
        this.$set(this.tocs[i], 'active', id === this.tocs[i].href)
      }
      this.$nextTick(() => {
        const element = document.querySelector('.toc-list')
        const clientHeight = element.clientHeight
        const offsetTop = document.querySelector(`li[data-id='${id}']`)?.offsetTop || 0
        if (element.scrollTop + clientHeight < offsetTop || element.scrollTop > offsetTop) {
          element.scrollTop = Math.max(offsetTop - clientHeight, 0)
        }
      })
    },

    handlePreview (file) {
      this.$store.commit('UPDATE_PREVIEW_IMGSRC', file.url)
    },

    bindFocusIntersectObserver () {
      const targetNodeElement = document.querySelector('.auth-charts .focus-user')
      if (targetNodeElement) {
        const observer = new IntersectionObserver((entries, _observer) => {
          entries.forEach(entry => {
            this.focusBtnVisible = entry.intersectionRatio < 1
          })
        }, { threshold: 1, rootMargin: '-80px 0px 0px 0px' })
        observer.observe(targetNodeElement)
      }
    }
  },

  computed: {
    ...mapState({
      userInfo: (state) => state.userInfo
    })
  },

  beforeDestroy () {
    document.body.oncopy = (event) => {
      event.preventDefault()
      const copyFont = window.getSelection(0).toString()
      if (event.clipboardData) {
        return event.clipboardData.setData('text', copyFont)
      } else {
        return window.clipboardData.setData('text', copyFont)
      }
    }
    removeScriptAndLink()
  }
}
</script>

<style lang="less" scoped>
.article-content-block {
  margin: 12px auto 0;
  display: flex;
  align-items: stretch;
  padding-bottom: 30px;
  position: relative;
  .author-info-block {
    background: #fff;
    border-radius: 4px;
    padding: 20px;
    .author-info {
      display: flex;
      align-items: center;
      .nickname {
        margin-left: 16px;
        p {
          font-size: 14px;
          color: #000;
        }
        span {
          counter-reset: #999;
          font-size: 12px;
          opacity: .5;
        }
      }
    }
    .auth-charts {
      padding-top: 8px;
      margin-top: 10px;
      .user-statistic {
        display: flex;
        margin-top: 8px;
        &-item {
          flex: 1;
          text-align: center;
          p {
            color: #999;
          }
          span {
            color: #4c4c4c;
            font-size: 16px;
          }
        }
      }
      .focus-user {
        display: flex;
        justify-content: space-between;
        margin-top: 18px;
        button {
          padding: 0;
          width: 110px;
        }
      }
    }
  }
  .article-info-block {
    flex: 1;
    width: 0;
    .article-content {
      background: #fff;
      padding: 30px 50px;
      border-radius: 4px;
      .article-suspended-panel {
        position: fixed;
        margin-left: -130px;
        top: 160px;
        .modal {
          position: relative;
          width: 48px;
          height: 48px;
          background-color: #fff;
          background-position: 50%;
          background-repeat: no-repeat;
          background-size: contain;
          border-radius: 50%;
          box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.04);
          cursor: pointer;
          text-align: center;
          line-height: 48px;
          margin: 0 auto 20px;
          transition: .3s;
          &.no-bg {
            background: rgb(236,237,241);
            span {
              color: #8a919f !important;
            }
          }
          &.hidden {
            width: 0;
            height: 0;
            opacity: 0;
            margin-bottom: 0;
            overflow: hidden;
          }
          .focus-text {
            position: absolute;
            padding: 0 6px;
            border-radius: 12px;
            color: #fff;
            background: var(--primary-color);
            font-style: normal;
            white-space: nowrap;
            line-height: normal;
            font-size: 12px;
            bottom: -6px;
            left: 50%;
            transform: translateX(-50%);
          }
          a {
            display: block;
            width: 100%;
            height: 100%;
          }
          span {
            color: #8a919f;
            font-size: 20px;
          }
          &:hover {
            span {
              color: #515767;
            }
          }
          em {
            position: absolute;
            top: 0;
            left: 75%;
            height: 17px;
            line-height: 17px;
            padding: 0 5px;
            border-radius: 9px;
            font-size: 11px;
            text-align: center;
            white-space: nowrap;
            background-color: #c2c8d1;
            color: #fff;
            font-style: normal;
          }
          &.liker {
            span {
              color: var(--primary-color);
            }
            em {
              background: var(--primary-color);
            }
          }
          &.collecter {
            span {
              color: #f1ca0a;
            }
          }
        }
      }
      .title {
        font-size: 28px;
        margin-bottom: 20px;
        color: #000;
        font-weight: bold;
      }
      .user-model {
        display: flex;
        align-items: center;
        margin-bottom: 20px;
        .info {
          flex: 1;
          margin-left: 10px;
          .time {
            color: #999;
          }
          .nickname {
            display: flex;
            align-items: center;
            a {
              color: #4c4c4c;
            }
          }
        }
      }
      .article-tag {
        margin-top: 20px;
        span {
          font-size: 11px;
          padding: 0 10px;
          border-radius: 4px;
          line-height: 20px;
          display: inline-block;
          cursor: default;
          color: var(--primary-color);
          background-color: rgb(96 244 186 / 6%);
          margin-right: 8px;
        }
      }
      .reprint-statement {
        padding: 16px 14px;
        background-color: rgb(96 244 186 / 6%);
        margin-top: 24px;
        border-radius: 2px;
        .title {
          font-size: 13px;
          margin-bottom: 4px;
          color: #333;
        }
        .label-item {
          display: flex;
          align-items: center;
          font-size: 13px;
          padding: 3px 0;
          .label {
            color: #999;
          }
          .value {
            em {
              font-style: normal;
            }
            a {
              color: var(--primary-color);
            }
          }
        }
      }
    }
  }
  .sticky-modal {
    position: sticky;
    top: 73px;
  }
  .tocs-info-block {
    background: #fff;
    border-radius: 4px;
    margin-top: 20px;
    padding: 10px 4px 10px 0;
    .title {
      padding: 0 20px;
      line-height: 36px;
      color: #000;
    }
    .toc-list {
      list-style: none;
      margin: 0;
      max-height: 360px;
      overflow-y: scroll;
      margin-top: 10px;
      padding: 0;
      &-item {
        line-height: 32px;
        padding: 0 20px 0 10px;
        position: relative;
        font-size: 13px;
        span {
          color: #000;
          padding: 0 12px;
          border-radius: 4px;
          display: block;
          overflow: hidden;
          white-space: nowrap;
          text-overflow: ellipsis;
          cursor: pointer;
          &:hover {
            color: var(--primary-color) !important;
          }
        }
        &.active {
          &:before {
            content: "";
            position: absolute;
            top: 50%;
            left: 4px;
            width: 6px;
            height: 6px;
            transform: rotate(45deg);
            margin-top: -3px;
            background: var(--primary-color);
          }
          span {
            color: var(--primary-color) !important;
          }
        }
        &.h2, &.h3 {
          font-size: 12px;
          line-height: 28px;
        }
        &.h2 {
          span {
            color: #4c4c4c;
          }
        }
        &.h3 {
          span {
            color: #999;
          }
        }
      }
    }
  }
  .ant-empty {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    margin-top: 200px;
    ::v-deep&-description {
      font-size: 18px;
    }
  }
}

</style>
